// MVVM对象声明
export class View {
    // $el: HTMLElement;
    $options: Object;
    $methods: Object;
    $data:Object;
    constructor(option: any) {
        let vm = this;
        this.$options = option;
        // this.$el = document.querySelector(option.el);
        this.$methods = option.methods;
        // reactive
        this.$data = defineReactive(option.data);
        // touch
        Object.keys(option.methods).forEach(key => effect(option.methods[key].bind(vm)));
    }
}
// ----->依赖

// 依赖类定义
class Deps {
    static runtimeTarget: RuntimeFunction = null;// 正在运行的函数
    subscribers: Function[];// 依赖函数（订阅者）列表
    constructor() {
        this.subscribers = [];// 初始化订阅者列表
    }
    // 添加数据的依赖
    depend() {
        // 保证订阅者的唯一性且只记录函数运行时的依赖
        if (Deps.runtimeTarget instanceof Function && !this.subscribers.includes(Deps.runtimeTarget)) {
            this.subscribers.push(Deps.runtimeTarget);
        }
    }
    // 更新所有依赖
    notify() {
        this.subscribers.forEach(sub => sub());
    }
}
// 收集运行时依赖函数
function effect(eff: Function) {
    Deps.runtimeTarget = eff;
    Deps.runtimeTarget();
    Deps.runtimeTarget = null;
}

// ----->观察
// 响应式对象方法定义
function defineReactive<T extends Object>(obj: T) {
    const propMap = new Map<string, Deps>();// 依赖存储，属性名-->依赖列表
    return new Proxy(obj, {
        get(obj, key: string, receiver) {
            if (!propMap.get(key)) propMap.set(key, new Deps());// 如果该属性没有依赖列表就创建新的依赖列表
            (propMap.get(key) as Dep).depend();// 添加当前依赖
            return Reflect.get(obj, key, receiver);
        },
        set(obj, key: string, value: any, receiver) {
            let res = Reflect.set(obj, key, value, receiver);// 记得先设置Reflect,值才会更新
            if (propMap.get(key)) (propMap.get(key) as Deps).notify();// 如果存在依赖列表，就更新触发所有依赖
            return res;
        }
    });
}



// 测试用例
let total3 = 0;
let myApp = new View({
    el: "#app",
    data:{
        price:15,
        quatity:20
    },
    methods: {
        calPrice(this:any){
            total3 = this.$data.price*this.$data.quatity;
        }
    }
});
console.log(total3);// 300
(myApp.$data as any).price = 20;
console.log(total3);// 400
(myApp.$data as any).quatity = 40;
console.log(total3);// 800
// 方法变成的也是响应式的
